

#include "StdAfx.h"
#include "ArchiveCallback.h"
#include <errno.h>
#include <stdio.h>

static const wchar_t *kEmptyFileAlias = L"[Content]";

using namespace NWindows;
using namespace NFile;
using namespace NDir;

static HRESULT IsArchiveItemProp(IInArchive *archive, UInt32 index, PROPID propID, bool &result)
{
	NCOM::CPropVariant prop;
	RINOK(archive->GetProperty(index, propID, &prop));
	if (prop.vt == VT_BOOL)
		result = VARIANT_BOOLToBool(prop.boolVal);
	else if (prop.vt == VT_EMPTY)
		result = false;
	else
		return E_FAIL;
	return S_OK;
}
static HRESULT IsArchiveItemFolder(IInArchive *archive, UInt32 index, bool &result)
{
	return IsArchiveItemProp(archive, index, kpidIsDir, result);
}
STDMETHODIMP CArchiveOpenCallback::SetTotal(const UInt64 * /* files */, const UInt64 * /* bytes */)
{
	return S_OK;
}
STDMETHODIMP CArchiveOpenCallback::SetCompleted(const UInt64 * /* files */, const UInt64 * /* bytes */)
{
	return S_OK;
}
STDMETHODIMP CArchiveOpenCallback::CryptoGetTextPassword(BSTR *password)
{
	if (!m_PasswordIsDefined)
	{
		// You can ask real password here from user
		// Password = GetPassword(OutStream);
		// PasswordIsDefined = true;
		return E_ABORT;
	}
	return StringToBstr(m_Password, password);
}
void CArchiveExtractCallback::Init(IInArchive *archiveHandler, const FString &directoryPath)
{
	m_NumErrors = 0;
	_archiveHandler = archiveHandler;
	_directoryPath = directoryPath;
	NName::NormalizeDirPathPrefix(_directoryPath);
}
STDMETHODIMP CArchiveExtractCallback::SetTotal(UInt64 /* size */)
{
	return S_OK;
}
STDMETHODIMP CArchiveExtractCallback::SetCompleted(const UInt64 * /* completeValue */)
{
	return S_OK;
}
STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, ISequentialOutStream **outStream, Int32 askExtractMode)
{
	*outStream = 0;
	_outFileStream.Release();
	{
		// Get Name
		NCOM::CPropVariant prop;
		RINOK(_archiveHandler->GetProperty(index, kpidPath, &prop));

		UString fullPath;
		if (prop.vt == VT_EMPTY)
			fullPath = kEmptyFileAlias;
		else
		{
			if (prop.vt != VT_BSTR)
				return E_FAIL;
			fullPath = prop.bstrVal;
		}
		_filePath = fullPath;
	}

	if (askExtractMode != NArchive::NExtract::NAskMode::kExtract)
		return S_OK;

	{
		// Get Attrib
		NCOM::CPropVariant prop;
		RINOK(_archiveHandler->GetProperty(index, kpidAttrib, &prop));
		if (prop.vt == VT_EMPTY)
		{
			_processedFileInfo.Attrib = 0;
			_processedFileInfo.AttribDefined = false;
		}
		else
		{
			if (prop.vt != VT_UI4)
				return E_FAIL;
			_processedFileInfo.Attrib = prop.ulVal;
			_processedFileInfo.AttribDefined = true;
		}
	}

	RINOK(IsArchiveItemFolder(_archiveHandler, index, _processedFileInfo.isDir));

	{
		// Get Modified Time
		NCOM::CPropVariant prop;
		RINOK(_archiveHandler->GetProperty(index, kpidMTime, &prop));
		_processedFileInfo.MTimeDefined = false;
		switch (prop.vt)
		{
		case VT_EMPTY:
			// _processedFileInfo.MTime = _utcMTimeDefault;
			break;
		case VT_FILETIME:
			_processedFileInfo.MTime = prop.filetime;
			_processedFileInfo.MTimeDefined = true;
			break;
		default:
			return E_FAIL;
		}
	}
	{
		// Get Size
		NCOM::CPropVariant prop;
		RINOK(_archiveHandler->GetProperty(index, kpidSize, &prop));
		UInt64 newFileSize;
		/* bool newFileSizeDefined = */ ConvertPropVariantToUInt64(prop, newFileSize);
	}

	FString fullProcessedPath;

	//如果路径获取返回非0，表示不释放当前文件
	if (m_pfn)
	{
		wchar_t ch[260] = {0};
		if (0 != m_pfn(index, ch, 260, m_param))
		{
			return S_OK;
		}
		fullProcessedPath = ch;
	}
	else
	{
		fullProcessedPath = _directoryPath + us2fs(_filePath);
	}
	// Create folders for file
	int slashPos = fullProcessedPath.ReverseFind_PathSepar();
	if (slashPos >= 0)
		CreateComplexDir(fullProcessedPath.Left(slashPos));

	_diskFilePath = fullProcessedPath;
	if (_processedFileInfo.isDir)
	{
		CreateComplexDir(fullProcessedPath);
	}
	else
	{
		NFind::CFileInfo fi;
		if (fi.Find(fullProcessedPath))
		{
			if (!DeleteFileAlways(fullProcessedPath))
			{
				return S_OK;
			}
		}

		_outFileStreamSpec = new COutFileStream;
		CMyComPtr<ISequentialOutStream> outStreamLoc(_outFileStreamSpec);
		if (!_outFileStreamSpec->Open(fullProcessedPath, CREATE_ALWAYS))
		{
			return S_OK;
		}
		_outFileStream = outStreamLoc;
		*outStream = outStreamLoc.Detach();
	}
	return S_OK;
}
STDMETHODIMP CArchiveExtractCallback::PrepareOperation(Int32 askExtractMode)
{
	_extractMode = false;
	switch (askExtractMode)
	{
	case NArchive::NExtract::NAskMode::kExtract:
		_extractMode = true;
		break;
	};
	switch (askExtractMode)
	{
	case NArchive::NExtract::NAskMode::kExtract:
		break;
	case NArchive::NExtract::NAskMode::kTest:
		break;
	case NArchive::NExtract::NAskMode::kSkip:
		break;
	};
	return S_OK;
}
STDMETHODIMP CArchiveExtractCallback::SetOperationResult(Int32 operationResult)
{
	switch (operationResult)
	{
	case NArchive::NExtract::NOperationResult::kOK:
		break;
	default:
	{
		m_NumErrors++;
		switch (operationResult)
		{
		case NArchive::NExtract::NOperationResult::kUnsupportedMethod:
			break;
		case NArchive::NExtract::NOperationResult::kCRCError:
			break;
		case NArchive::NExtract::NOperationResult::kDataError:
			break;
		case NArchive::NExtract::NOperationResult::kUnavailable:
			break;
		case NArchive::NExtract::NOperationResult::kUnexpectedEnd:
			break;
		case NArchive::NExtract::NOperationResult::kDataAfterEnd:
			break;
		case NArchive::NExtract::NOperationResult::kIsNotArc:
			break;
		case NArchive::NExtract::NOperationResult::kHeadersError:
			break;
		}
	}
	}

	if (_outFileStream)
	{
		if (_processedFileInfo.MTimeDefined)
			_outFileStreamSpec->SetMTime(&_processedFileInfo.MTime);
		RINOK(_outFileStreamSpec->Close());
	}
	_outFileStream.Release();
	if (_extractMode && _processedFileInfo.AttribDefined)
		SetFileAttrib(_diskFilePath, _processedFileInfo.Attrib);
	return S_OK;
}
STDMETHODIMP CArchiveExtractCallback::CryptoGetTextPassword(BSTR *password)
{
	if (!m_PasswordIsDefined)
	{
		// You can ask real password here from user
		// Password = GetPassword(OutStream);
		// PasswordIsDefined = true;
		return E_ABORT;
	}
	return StringToBstr(m_Password, password);
}

STDMETHODIMP CArchiveUpdateCallback::SetTotal(UInt64 /* size */)
{
	return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::SetCompleted(const UInt64 * /* completeValue */)
{
	return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetUpdateItemInfo(UInt32 index, Int32 *newData, Int32 *newProperties, UInt32 *indexInArchive)
{
	const CDirItem &dirItem = (*m_DirItems)[index];
	if (newData)
		*newData = dirItem.isNewdata;
	if (newProperties)
		*newProperties = dirItem.isNewproperties;
	if (indexInArchive)
		*indexInArchive = dirItem.indexInArchive;
	return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value)
{
	NCOM::CPropVariant prop;

	if (propID == kpidIsAnti)
	{
		prop = false;
		prop.Detach(value);
		return S_OK;
	}

	{
		const CDirItem &dirItem = (*m_DirItems)[index];
		switch (propID)
		{
		case kpidPath:
			prop = dirItem.Name;
			break;
		case kpidIsDir:
			dirItem.isNewdata ? prop = dirItem.isDir() : false;
			break;
		case kpidSize:
			dirItem.isNewdata ? prop = dirItem.Size : false;
			break;
		case kpidAttrib:
			dirItem.isNewdata ? prop = dirItem.Attrib : false;
			break;
		case kpidCTime:
			dirItem.isNewdata ? prop = dirItem.CTime : false;
			break;
		case kpidATime:
			dirItem.isNewdata ? prop = dirItem.ATime : false;
			break;
		case kpidMTime:
			dirItem.isNewdata ? prop = dirItem.MTime : false;
			break;
		}
	}
	prop.Detach(value);
	return S_OK;
}

HRESULT CArchiveUpdateCallback::Finilize()
{
	if (m_NeedBeClosed)
	{
		m_NeedBeClosed = false;
	}
	return S_OK;
}
static void GetStream2(const wchar_t *name)
{
	if (name[0] == 0)
		name = kEmptyFileAlias;
}
STDMETHODIMP CArchiveUpdateCallback::GetStream(UInt32 index, ISequentialInStream **inStream)
{
	RINOK(Finilize());

	const CDirItem &dirItem = (*m_DirItems)[index];
	GetStream2(dirItem.Name);

	if (dirItem.isDir())
		return S_OK;

	{
		CInFileStream *inStreamSpec = new CInFileStream;
		CMyComPtr<ISequentialInStream> inStreamLoc(inStreamSpec);
		FString path = m_DirPrefix + dirItem.FullPath;
		if (!inStreamSpec->Open(path))
		{
			DWORD sysError = errno;
			m_FailedCodes.Add(sysError);
			m_FailedFiles.Add(path);

			return S_FALSE;
		}
		*inStream = inStreamLoc.Detach();
	}
	return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::SetOperationResult(Int32 /* operationResult */)
{
	m_NeedBeClosed = true;
	return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetVolumeSize(UInt32 index, UInt64 *size)
{
	if (m_VolumesSizes.Size() == 0)
		return S_FALSE;
	if (index >= (UInt32)m_VolumesSizes.Size())
		index = m_VolumesSizes.Size() - 1;
	*size = m_VolumesSizes[index];
	return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::GetVolumeStream(UInt32 index, ISequentialOutStream **volumeStream)
{
	wchar_t temp[16];
	ConvertUInt32ToString(index + 1, temp);
	UString res = temp;
	while (res.Len() < 2)
		res.InsertAtFront(L'0');
	UString fileName = m_VolName;
	fileName += L'.';
	fileName += res;
	fileName += m_VolExt;
	COutFileStream *streamSpec = new COutFileStream;
	CMyComPtr<ISequentialOutStream> streamLoc(streamSpec);
	if (!streamSpec->Create(us2fs(fileName), false))
		return errno;
	*volumeStream = streamLoc.Detach();
	return S_OK;
}
STDMETHODIMP CArchiveUpdateCallback::CryptoGetTextPassword2(Int32 *passwordIsDefined, BSTR *password)
{
	if (!m_PasswordIsDefined)
	{
		if (m_AskPassword)
		{
			// You can ask real password here from user
			// Password = GetPassword(OutStream);
			// PasswordIsDefined = true;
			return E_ABORT;
		}
	}
	*passwordIsDefined = BoolToInt(m_PasswordIsDefined);
	return StringToBstr(m_Password, password);
}